<?php
/**
 * Zhub - A PHP Framework For API Developer
 *
 * This file is part of the Zhub package.
 *
 * (c) YiLong Fan <fantiq@163.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author fanyilong <fantiq@163.com>
 * @copyright 2017 fanyilong. All rights reserved.
 * @package Zhub
 * @date 18-3-1
 */

namespace Zhub\Prototype\Variable;


use Zhub\Prototype\Variable\Exceptions\InvalidArgumentException;
use Zhub\Prototype\Variable\Exceptions\InvalidValueException;

class Type
{
    const TYPE_OPTIONAL_FLAG = '?';

    private $id = null;

    private $optional = false;

    private $value = null;

    public function __construct(?int $id, bool $optional = false, string $value = null)
    {
        if ($id === null && $value == null) {
            throw new InvalidArgumentException();
        }
        // 需要从给定的值推断出类型
        if ($id === null) {
            $typeId = self::valueToType($value);
            if (!$typeId) {
                throw new InvalidValueException($value);
            }
            $id = $typeId;
        }

        $this->id = $id;
        $this->optional = $optional;
        $this->value = $value;
    }

    public function getId():int
    {
        return $this->id;
    }

    public function getValue():?string
    {
        return $this->value;
    }

    /**
     * 如果是 clazz 类型需要调用方自己处理
     * @return null|string
     */
    public function getName():?string
    {
        if ($this->id == TypeEnum::CLAZZ) {
            return null;
        }
        return ($this->optional ? self::TYPE_OPTIONAL_FLAG : '' ) .
            TypeEnum::name($this->id);
    }

    /**
     * 通过字符串形式的值推断出这个值应该是什么类型
     * @param string $value
     * @return int|null
     */
    public static function valueToType(string $value)
    {
        $type = null;
        if (is_numeric($value)) {
            $pattern = '/[0-9]+\.[0-9]+/';
            $type = preg_match($pattern, $value) ? TypeEnum::FLOAT : TypeEnum::INT;
        }
        elseif (preg_match('/^(true|false)$/i', $value)) {
            $type = TypeEnum::BOOL;
        }
        elseif (preg_match('/^"[^"]*"$|^\'[^\']*\'$/', $value)) {
            $type = TypeEnum::STRING;
        }
        elseif (preg_match('/^\[[^\]]*\]$/', $value)) {
            $type = TypeEnum::ARRAY;
        }
        elseif (preg_match('/^null$/i', $value)) {
            $type = TypeEnum::NULL;
        }
        elseif (class_exists($value)) {
            $type = TypeEnum::CLAZZ;
        }
        return $type;
    }

    public static function newByValue(string $value, bool $optional = false)
    {
        return new self(null, $optional, $value);
    }

    public static function newByTypeId(int $id, bool $optional = false)
    {
        return new self($id, $optional);
    }
}
